package com;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Random;
import de.dfki.util.xmlrpc.XmlRpc;

public class PuppetMaster {
	// Configuration of the Puppet master
	int port = 10000;
	int capacity = 100;
	String host = "localhost";
	String name = "PuppetMaster";
	
	ArrayList<Peer> peers = new ArrayList<Peer>(1000);
	Peer local_peer = null;
	
	public PuppetMaster() {
		local_peer = new Peer(name, host, port, capacity);
	}
	
	public void print_options() {
		System.out.println("Puppet Master\n");
		System.out.println("commands:");
		System.out.println("    [l]oad      Load script from file");
		System.out.println("    [s]pawn     Spawn a peer");
		System.out.println("    [n]spawn    Spawn several peers");
		System.out.println("    [c]ontrol   Remote control a peer");
		System.out.println("    [x]         Exit the program");
	}
	
	public void error(String err) {
		System.out.println("ERROR: " + err);
	}

	/* Helper class to spawn peers in own thread */
	public class Spawner implements Runnable {

		private String addr, name;
	    private int port, capa;

	    public Spawner(String name, String addr, int port, int capa) {
	    	this.name = name;
	    	this.addr = addr;
	    	this.port = port;
	    	this.capa = capa;
	    }
	    public void run() {
	        // code in the other thread, can reference "var" variable
			Peer p = new Peer(name, addr, port, capa);
			p.startServer();
			peers.add(p);
	    }
	}
	
	public void evaluate(String expr) {
		/*
		 * Evaluates a single string by looking at the first character
		 * @TODO: Move the contents of each case to a separate function
		 */
		String[] argv = expr.split(" ");
		char t = argv[0].toLowerCase().charAt(0);
		int argc = argv.length - 1;
		
		switch(t) {
		// SPAWN(name, addr, port, capa)
		case 'S':
		case 's':
			if(argv[0].equals("sleep")) {
				try {
					Thread.sleep(Long.parseLong(argv[1]));
				} catch(Exception e) { e.printStackTrace(); }
				break;
			} 
			if(argc != 4) error("syntax: spawn name address port capacity");
			else {
				System.out.println("SPAWN: " + argv[1] + " " + argv[2] + ":" + argv[3] + " capacity " + argv[4]);
				//Peer p = new Peer(argv[1], argv[2], Integer.parseInt(argv[3]), Integer.parseInt(argv[4]));
				//p.startServer();
				//peers.add(p);
				Spawner new_peer = new Spawner(argv[1], argv[2], Integer.parseInt(argv[3]), Integer.parseInt(argv[4]));
				new_peer.run();
			}
			System.out.println("Spawning");
			break;

			// RESET traffic stats 
		case 'r':
			for(Peer p : peers) {
				p.packet_received = 0;
				p.packet_sent = 0;
			}
			// no break!! FALL THROUGH TO NEXT CASE
		// TRAFFIC report traffic, total packets received/sent for all peers
		case 't':
			int rx = 0, tx = 0;
			for(Peer p : peers) {
				rx += p.packet_received;
				tx += p.packet_sent;
			}
			System.out.println("Traffic report for all " + peers.size() + " nodes:\nRx: " + rx + ", Tx: " + tx);
			System.out.println("type 'reset' to clear");
			break;	
		
	
		// NSPAWN(n, name, addr, port, capa)
		//    capa can be "10-50" which will pick random in that range
		case 'n':
		case 'N':
			if(argc != 5) error("syntax: nspawn number name address port capacity");
			else {
				String name, addr;
				int i, port, capa, npeers;
				int rand_min = 10, rand_max = 10;
				npeers = Integer.parseInt(argv[1]);
				
				// pick random ports in a certain range
				boolean shuffle_ports = argv[5].contains("-");

				// spawn all those peers
				for(i = 0; i < npeers; ++i) {
					name = argv[2] + i;
					addr = argv[3];
					port = Integer.parseInt(argv[4]) + i;
					
					if(shuffle_ports) {
						shuffle_ports = true;
						String minmax[] = argv[5].split("-");
						rand_min = Integer.parseInt(minmax[0]);
						rand_max = Integer.parseInt(minmax[1]);
						Random rand = new Random();
						capa = rand.nextInt(rand_max - rand_min + 1) + rand_min;
					} else {
						capa = Integer.parseInt(argv[5]);
					}
					
					System.out.println("Spawning: " + name + " " + addr + ":" + port + " capacity " + capa);
					com.PuppetMaster.Spawner new_peer = new Spawner(name, addr, port, capa);
					new_peer.run();
				}
			}
			break;
		// CONTROL(addr, port, cmd)
		case 'c':
		case 'C':
			if(argc < 3) error("syntax: control addr port cmd");
			else {
				int cmd_index = expr.indexOf(argv[3]);
				String addr = argv[1];
				String cmd = expr.substring(cmd_index);
				int from = 0, to = 0;
				if(argv[2].contains("-")) {
					String[] stra = argv[2].split("-");
					from = Integer.parseInt(stra[0]);
					to = Integer.parseInt(stra[1]);
				} else
					from = to = Integer.parseInt(argv[2]);
				int port = from;
				while(from <= to) {
					port = from;
					++from;
					RPCApi remote_api = XmlRpc.createClient(RPCApi.class, "Peer", addr, port);
					String result = remote_api.cmd(cmd);
					System.out.println(result);
					try {
						Thread.sleep(100);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			}
			break;
		// Exit
		case 'x':
		case 'X':
			System.out.println("EXIT");
			return;
		case 'l':
		case 'L':
			try {
				int filename_index = expr.indexOf(argv[1]);
				String filename =  expr.substring(filename_index);
				System.out.println("Load input script from file: '" + filename + "'");
	
			        StringBuffer fileData = new StringBuffer();
			        BufferedReader reader = new BufferedReader(new FileReader(filename));
			        char[] buf = new char[1024];
			        int numRead=0;
			        while((numRead=reader.read(buf)) != -1) {
			            String readData = String.valueOf(buf, 0, numRead);
			            fileData.append(readData);
			        }
			        reader.close();
			        String everything = fileData.toString();
				    String[] lines = everything.split("\n");
				    
				    int line_num = 1;
					for(String l : lines) {
						if(l.trim().startsWith("#") || l.trim().length() == 0) // ignore comments
							continue;
						try {
						evaluate(l.trim());
						++line_num;
						} catch (Exception e) {
					    	error("evaluating " + filename + " line " + line_num + ": '" + l + "'");
					    }
					}
				    
			} catch (Exception e) {
				e.printStackTrace();
			}
			break;
		default:
			System.out.println("ERROR: unrecognized command: '" + argv[0] + '"');
			print_options();
		}
	}

	public static void main(String args[]) {
		if(args.length == 2 && args[0].equals("-f")) {
			System.out.println("load from file: '" + args[1] + "'");
			return;
		}

		PuppetMaster pm = new PuppetMaster();
		
		pm.print_options();

		String input;
		BufferedReader stdin = new BufferedReader(new InputStreamReader(System.in));
		while(true) {
			try {
				System.out.print("\n> ");
				input = stdin.readLine();
				if(input.length() > 0)
					pm.evaluate(input);
				
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
}

